#include <algorithm>
#include <functional>
#include <AL/al.h>
#include <OgreOSMScene.h>

#include "include/VectorTest.h"
#include "include/App.h"

#include "include/Scripting.h"
#include "include/NetManager.h"
#include "include/DotScene.h"
#include "include/MagAcc.h"
#include "include/VectorObject.h"


using namespace Ogre;
using namespace Ei;

App* App::theApp = NULL;

// constructor destructor and setup* {{{
App::App(Ei::Scripting* scr,
	 const char * scene_fname) :
	mScripting(scr)
{
	eilog("starting the App");
	// assert(theApp == NULL)
	theApp = this;

	mStatsOn = true;
	mNumScreenShots = 0;
	mTimeUntilNextToggle = 0;

	// split scene path into dir and filename
	// XXX use boost path
	String path(scene_fname), dir_name, file_name;
	size_t last_slash =  path.find_last_of("\\/");
	if (last_slash != String::npos) {
		dir_name = path.substr(0, last_slash);
		file_name = path.substr(last_slash + 1);
	} else {
		dir_name = String(".");
		file_name = path;
	}

	Ogre::Root * root = new Ogre::Root("", "", "Ogre.log");
	setupResources(dir_name);
	setupRenderSystem(root);
	root->initialise(false);
	root->addFrameListener(this);

	// create scene manager and scene
	mSceneMgr = root->createSceneManager(ST_GENERIC); 
	
	setupMainWindow(root);
	eilog("setup main window");
	// XXX ?
	TextureManager::getSingleton().setDefaultNumMipmaps(5);

	// this must be here (XXX why?)
	Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

	mDebugOverlay = OverlayManager::getSingleton().
		getByName("Core/DebugOverlay");
	showDebugOverlay(true);	

        loadScene(file_name, root);
        setupCamera();
        
        // all meshes and scens are loaded.
        // create Magnetometer and Accmeter object
	mMagAcc = new Ei::MagAcc();
	setupRotationBalls();
	setupLook();
	
	// Setup network
	mNetMgr = new Ei::NetManager();
	mNetMgr->SetupMethods();

//	mNetMgr->sendMessage("/sios/sensors/accmag/acc/listen");
//	mNetMgr->sendMessage("/sios/sensors/accmag/mag/listen");
	
	// setup Gui, incoporates the event manager
	mGui = new Gui(mSceneMgr, mWindow);
	mGui->init();
       	root->addFrameListener(mGui);
	
	eilog("start rendering");
	
	root->startRendering();   
}

App::~App()
{

	mNetMgr->sendMessage("/sios/sensors/accmag/mag/silence");
	mNetMgr->sendMessage("/sios/sensors/accmag/acc/silence");
//	PlatformManager::getSingleton().destroyInputReader( mInputDevice );
	delete mNetMgr;
	delete mGui;
	// XXX killall life
}

void App::loadScene(Ogre::String file_name, Ogre::Root * root)
{
	String ext(file_name, file_name.rfind('.'));
	
	if (ext == ".OSM") {
		// load the scene
		OSMScene oScene;
		oScene.initialise(file_name.c_str(), NULL);
		oScene.createScene();
		mSceneMgr = oScene.getSceneManager();
	} else {
		// XXX try other (e.g. exterior close)?
		mSceneMgr = root->createSceneManager(ST_GENERIC); 
		DotSceneLoader dotScene(NULL);
		dotScene.parseDotScene(file_name, "General", mSceneMgr);
	}
	
}

void App::setupResources(String dir_name)
{
	Ogre::ResourceGroupManager & resMan = 
		Ogre::ResourceGroupManager::getSingleton();
	resMan.addResourceLocation(MAIN_RESOURCES_DIR, "FileSystem", "General" );
	resMan.addResourceLocation(MAIN_RESOURCES_DIR "/gui", 
				   "FileSystem", "General");		   				   
	resMan.addResourceLocation(dir_name, "FileSystem", "General");
}

void App::setupRenderSystem(Ogre::Root * root)
{
	String rs_name(mScripting->getTableCString("win", "renderSystem"));
	if (rs_name.find("Direct3D") != String::npos 
	    || rs_name.find("D3D") != String::npos) {
		rs_name = "Direct3D9 Rendering Subsystem";
		root->loadPlugin("RenderSystem_Direct3D9");
	} else { // default to OpenGL 
		rs_name = "OpenGL Rendering Subsystem";
		root->loadPlugin("RenderSystem_GL");
	}
	RenderSystem * rs = root->getRenderSystemByName(rs_name);
	if (!rs) {
		// XXX where is grace?
		eilog("Can't load rendering system: %s\n", rs_name.c_str());
		exit(1);
	}
	root->setRenderSystem(rs);
}



void App::setupMainWindow(Ogre::Root * root)
{
	NameValuePairList opts;
	mScripting->tableToPairs("win", &opts);
	mWindow = root->
		createRenderWindow("Vector test program",
				   mScripting->getTableInt("win", "width"),
				   mScripting->getTableInt("win", "height"),
				   mScripting->getTableBool("win", "fullscreen"),
				   &opts);
}

void App::setupCamera()
{
	eilog("creating camera");
	SceneNode * cam_node = mSceneMgr->createSceneNode("Camera");
	mSceneMgr->getRootSceneNode()->addChild(cam_node);
	
	SceneNode * camPitch_node = mSceneMgr->createSceneNode("Camera_PitchNode");
	cam_node->addChild(camPitch_node);
	
	eilog("added cam node");

	Camera * cam = mSceneMgr->createCamera("Mono camera");
	eilog("created camera");
	cam->setPosition(2,5,16);
	cam->setDirection(0,0,-1); 
	cam->setNearClipDistance(0.01); // changed near clipping to allow user fog mesh
	cam->setAutoAspectRatio(true);
	eilog("Setup cam");
	camPitch_node->attachObject(cam);
	eilog("Attached");
	Viewport* vp = mWindow->addViewport(cam);
	eilog("Added viewport");
	vp->setBackgroundColour(ColourValue(0, 0, 0));
}


void App::setupLook()
{
	mSceneMgr->setShadowTechnique(SHADOWTYPE_NONE);
}

void App::setupRotationBalls()
{

	// create rotate object 
        Entity* mEntity = mSceneMgr->createEntity("rotateobj", "Ybox.mesh");
        SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode("rotateobj");
        mEntity->setMaterialName("yellow");
        node->setScale(Vector3(3,3,3));
        node->setPosition(Vector3(-10,0,-10));
	node->attachObject(mEntity);
         
       
        // create Sphere
        SceneNode * rootnode = mSceneMgr->getRootSceneNode()->createChildSceneNode("rootbol");
        rootnode->setPosition(-10,0,0); 
        
        mEntity = mSceneMgr->createEntity("vecSphere", "bol.mesh");
        node = rootnode->createChildSceneNode("bol");
        node->attachObject(mEntity);
         
        // UP Front and right obj
        SceneNode * URFroot = mSceneMgr->getRootSceneNode()->createChildSceneNode("URFroot");
        URFroot->setPosition(0,0,-10); 
        
        
        mEntity = mSceneMgr->createEntity("UpSphere", "bol.mesh");
        node = URFroot->createChildSceneNode("Upbol");
        mEntity->setMaterialName("up");     
        node->setScale(Vector3(3,3,3));
        node->setPosition(0,0,4); 
        node->attachObject(mEntity);
        
        mEntity = mSceneMgr->createEntity("UpBalk", "Ybox.mesh");
        node = URFroot->createChildSceneNode("Upbalk");
        mEntity->setMaterialName("red");     
        node->attachObject(mEntity);
        
        mEntity = mSceneMgr->createEntity("FrontSpnere", "bol.mesh");
        node = URFroot->createChildSceneNode("Frontbol");
        mEntity->setMaterialName("front");     
        node->setScale(Vector3(3,3,3));
         node->setPosition(0,0,4);
        node->attachObject(mEntity);
        
        mEntity = mSceneMgr->createEntity("RightSphere", "bol.mesh");
        node = URFroot->createChildSceneNode("Rightbol");
        mEntity->setMaterialName("right");     
        node->setScale(Vector3(3,3,3));
         node->setPosition(0,0,4);
        node->attachObject(mEntity);
        
        mEntity = mSceneMgr->createEntity("UpSphere2", "bol.mesh");
        node = URFroot->createChildSceneNode("Upbol2");
        mEntity->setMaterialName("up");     
        node->setScale(Vector3(3,4,3));
        node->attachObject(mEntity);
        
        mEntity = mSceneMgr->createEntity("FrontSpnere2", "bol.mesh");
        node = URFroot->createChildSceneNode("Frontbol2");
        mEntity->setMaterialName("front");     
        node->setScale(Vector3(3,4,3));
        node->attachObject(mEntity);
        
        mEntity = mSceneMgr->createEntity("RightSphere2", "bol.mesh");
        node = URFroot->createChildSceneNode("Rightbol2");
        mEntity->setMaterialName("right");     
        node->setScale(Vector3(3,4,3));
        node->attachObject(mEntity);
 
        
        eilog("created objects");
        
}

bool usekalman = true;
bool App::processUnbufferedKeyInput(const FrameEvent& evt)
{
	// ------( misc )----------------------------------------------------
	if( mGui->getEventProcessor()->getInputReader()->isKeyDown( KC_ESCAPE) ) {
		return false;
	}
	

        
        static bool toggleAcc;
        static bool toggleMag;
        //(------------Toggle Acc en mag meter --------------------------)
	if (mGui->getEventProcessor()->getInputReader()->isKeyDown(KC_T) && mTimeUntilNextToggle <= 0) {
		
		if (!(toggleAcc))
		{
		mNetMgr->sendMessage("/sios/sensors/accmag/acc/listen");
		eilog("Listen Acc");
		mTimeUntilNextToggle = 1;		
		}
		else
		{
		mNetMgr->sendMessage("/sios/sensors/accmag/acc/silence");
		eilog("silence Acc");
		mTimeUntilNextToggle = 1;
		}
		toggleAcc = !toggleAcc;

	}
	if (mGui->getEventProcessor()->getInputReader()->isKeyDown(KC_Y) && mTimeUntilNextToggle <= 0) {
		
		if (!(toggleMag))
		{
		mNetMgr->sendMessage("/sios/sensors/accmag/mag/listen");
		eilog("Listen mag");
		mTimeUntilNextToggle = 1;
		}
		else
		{
		mNetMgr->sendMessage("/sios/sensors/accmag/mag/silence");
		eilog("silence mag");
		mTimeUntilNextToggle = 1;
		}
		toggleMag= !toggleMag;
	}
		
        if (mGui->getEventProcessor()->getInputReader()->isKeyDown(KC_SYSRQ) && mTimeUntilNextToggle <= 0) {
		char tmp[20];
		sprintf(tmp, "screenshot_%d.png", ++mNumScreenShots);
		mWindow->writeContentsToFile(tmp);
		mTimeUntilNextToggle = 0.5;
		mWindow->setDebugText(String("Wrote ") + tmp);
	}
        
	/////// calibrate sensor 
	
	if (mGui->getEventProcessor()->getInputReader()->isKeyDown(KC_C) && mTimeUntilNextToggle <= 0) {
		mNetMgr->sendMessage("/sios/sensors/accmag/mag/listen");
		mNetMgr->sendMessage("/sios/sensors/accmag/mag/calibrate 0", true);
		mTimeUntilNextToggle = 0.5;
	}
	

	if (mGui->getEventProcessor()->getInputReader()->isKeyDown(KC_V) && mTimeUntilNextToggle <= 0) {
		mMagAcc->calibrateSensor();		
		mTimeUntilNextToggle = 0.5;		
	} 
	if (mGui->getEventProcessor()->getInputReader()->isKeyDown(KC_B) && mTimeUntilNextToggle <= 0) {
		mMagAcc->calculateOffset(); 		
		mTimeUntilNextToggle = 0.5;		
	} 
	if (mGui->getEventProcessor()->getInputReader()->isKeyDown(KC_N) && mTimeUntilNextToggle <= 0) {
		mMagAcc->measureExtremes(mMagAcc->getGravity()); 		
		mTimeUntilNextToggle = 0.5;		
	} 
	
	if (mGui->getEventProcessor()->getInputReader()->isKeyDown(KC_M) && mTimeUntilNextToggle <= 0) {
		mMagAcc->calibrateSensor(); 		
		mTimeUntilNextToggle = 0.5;		
	} 
	if (mGui->getEventProcessor()->getInputReader()->isKeyDown(KC_K) && mTimeUntilNextToggle <= 0) {
		mMagAcc->useFilter = mMagAcc->useFilter + 1;
		if (mMagAcc->useFilter >=4)
		{
			mMagAcc->useFilter = 0;	
		}
		
		eilog("using filterset " + StringConverter::toString(mMagAcc->useFilter));
			
		mTimeUntilNextToggle = 0.5;		
	} 
	if (mGui->getEventProcessor()->getInputReader()->isKeyDown(KC_SPACE))
	{
	  mSceneMgr->getSceneNode("Camera")->setOrientation(mMagAcc->getOrientation());	
	}
		
	static int vecswitch = 0;
	if (mGui->getEventProcessor()->getInputReader()->isKeyDown(KC_TAB) && mTimeUntilNextToggle <= 0) {
	
		vecswitch = mMagAcc->showVectors(vecswitch);	
	 
	 mTimeUntilNextToggle = 0.1;			
	} 
	
	
        // Return true to continue rendering        
        return true;}

bool App::processUnbufferedMouseInput(const FrameEvent& evt)
{
/*
	mSceneMgr->getSceneNode("Camera")
		->yaw(Degree(-mInputDevice->getMouseRelativeX() * 0.2));
	mSceneMgr->getSceneNode("Camera_PitchNode")
		->pitch(Degree(-mInputDevice->getMouseRelativeY() * 0.2));
*/		
	return true;
}

void App::showDebugOverlay(bool show)
{
	if (mDebugOverlay) {
		if (show) {
			mDebugOverlay->show();
		} else {
			mDebugOverlay->hide();
		}
	}
}

// XXX we stink!!! 
void App::postWorldStep()
{

}

bool App::frameStarted(const FrameEvent& evt)
{
	static Real currentTime = 0;

	// We update all loaded animations each frame
	SceneManager::AnimationIterator animationIt =
		mSceneMgr->getAnimationIterator();

	while(animationIt.hasMoreElements()) {
		Animation* animation = animationIt.getNext();

		const Animation::NodeTrackList& trackList =
			animation->_getNodeTrackList();

		Animation::NodeTrackList::const_iterator it =
			trackList.begin();
		Animation::NodeTrackList::const_iterator iend =
			trackList.end();

		for(; it != iend; ++it) {
			const NodeAnimationTrack* track =
				it->second;
			track->getAssociatedNode()
				->resetToInitialState();
		}

		currentTime += evt.timeSinceLastFrame;
		animation->apply(currentTime);
	}

	// Call default
	if(mWindow->isClosed())
		return false;

	mGui->getEventProcessor()->getInputReader()->capture();

	if (mTimeUntilNextToggle >= 0)
		mTimeUntilNextToggle -= evt.timeSinceLastFrame;

	// 
	mNetMgr->pollSocket(1.0);
	mMagAcc->update(1.0);
	mMagAcc->getOrientation();
//
	
		
	     return (processUnbufferedKeyInput(evt) && processUnbufferedMouseInput(evt));
}


bool App::frameEnded(const FrameEvent& evt)
{
	//Real time = evt.timeSinceLastFrame;
	updateStats();
	return true;
}

void App::updateStats(void)
{
	OverlayManager * om = &OverlayManager::getSingleton();

	// update stats when necessary
	const RenderTarget::FrameStats& stats = mWindow->getStatistics();
	om->getOverlayElement("Ei/CurrFps")->
		setCaption("Current FPS: " +
			   StringConverter::toString( stats.lastFPS ));
	
	om->getOverlayElement("Ei/Triangles")->
		setCaption(mDebugString01);
		
	om->getOverlayElement("Ei/Snacks")->
		setCaption(mDebugString02);
	
	om->getOverlayElement("Ei/AverageFps")->
		setCaption(mDebugString03);


}
